unit maplistframeunit;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, ComCtrls, fgl;

type

  TIntegerList = specialize TFPGList<Integer>;
  TMapListChangedCallback = procedure (MapId: Integer) of object;

  { TMapListFrame }

  TMapListFrame = class(TFrame)
    MapListTreeView: TTreeView;
    procedure MapListTreeViewSelectionChanged(Sender: TObject);
  private
    Nodes: array of TTreeNode; //Nodes by map id
  public
    SelectionChangedCallback: TMapListChangedCallback;

    procedure FillMapInfoList;
    procedure ClearList;
    procedure SelectMapById(MapId: Integer);
  end;

implementation

uses fpjson, globals;

function CompareMapInfoIds(const Id1, Id2: Integer): Integer;
var
  MapInfos: TJSONArray;
  Order1: Integer = 0;
  Order2: Integer = 0;
  MapInfo1, MapInfo2: TJSONObject;
  Order1Json, Order2Json: TJSONNumber;
begin
  MapInfos := Game.MapInfos.Data;

  MapInfo1 := MapInfos.Objects[Id1];
  MapInfo2 := MapInfos.Objects[Id2];

  if MapInfo1.Find('order', Order1Json) then
    Order1 := Order1Json.AsInteger;
  if MapInfo2.Find('order', Order2Json) then
    Order2 := Order2Json.AsInteger;

  CompareMapInfoIds := Order1 - Order2;
end;

procedure TMapListFrame.MapListTreeViewSelectionChanged(Sender: TObject);
var
  MapId: PtrInt;
begin
  if MapListTreeView.Selected <> nil then begin
    {$WARN 4055 off : Conversion between ordinals and pointers is not portable}
      MapId := PtrInt(MapListTreeView.Selected.Data);
    {$WARN 4055 on : Conversion between ordinals and pointers is not portable}

    if SelectionChangedCallback <> nil then
      SelectionChangedCallback(MapId)
    else
      MapListTreeView.Selected.Text := ' test';
  end;
end;

procedure TMapListFrame.FillMapInfoList;
var
  MapInfos: TJSONArray;
  MapInfo: TJSONObject;
  SortedMapInfoIds: TIntegerList;
  I, ParentId: Integer;
  MapId: PtrInt; (* stored in Data, so needs to be compatible with a pointer *)
  ParentNode: TTreeNode;
  ExpandedJson: TJSONBoolean;
begin
  MapListTreeView.Items.Clear;
  MapInfos := Game.MapInfos.Data;

  SortedMapInfoIds := TIntegerList.Create;
  for I := 1 to MapInfos.Count - 1 do begin
    if not MapInfos[I].IsNull then
      SortedMapInfoIds.Add(I);
  end;
  SortedMapInfoIds.Sort(@CompareMapInfoIds);

  SetLength(Nodes, MapInfos.Count);
  Nodes[0] := MapListTreeView.Items.AddChild(nil, '[game]'); //TODO: replace [game] with game title
  for I := 0 to SortedMapInfoIds.Count - 1 do begin
    MapId := SortedMapInfoIds[I];
    MapInfo := MapInfos.Objects[MapId];

    ParentNode := nil;
    ParentId := MapInfo.Integers['parentId'];
    ParentNode := Nodes[ParentId];
    Assert(ParentNode <> nil, 'parentId must refer to an existing node');

    Nodes[MapId] := MapListTreeView.Items.AddChild(ParentNode,
                                                   MapInfo.Strings['name']);
    {$WARN 4055 off : Conversion between ordinals and pointers is not portable}
      Nodes[MapId].Data := Pointer(MapId);
    {$WARN 4055 on : Conversion between ordinals and pointers is not portable}
  end;

  Nodes[0].Expand(False);
  for I := 0 to SortedMapInfoIds.Count - 1 do begin
    MapId := SortedMapInfoIds[I];
    MapInfo := MapInfos.Objects[MapId];
    if MapInfo.Find('expanded', ExpandedJson) then
      if ExpandedJson.AsBoolean then
        Nodes[MapId].Expand(False);
  end;

  SortedMapInfoIds.Destroy;
end;

procedure TMapListFrame.ClearList;
begin
  MapListTreeView.Items.Clear;
  SetLength(Nodes, 0);
end;

procedure TMapListFrame.SelectMapById(MapId: Integer);
begin
  if (Nodes <> nil) and (MapId > 0) and (MapId < Length(Nodes))
                                 and (Nodes[MapId] <> nil) then
    MapListTreeView.Select(Nodes[MapId])
  else if (Nodes <> nil) and (Length(Nodes) > 0) then
    MapListTreeView.Select(Nodes[0])
  else
    MapListTreeView.Select([]);
end;

{$R *.lfm}

end.

